package planninglanguage.plans;

import java.util.Iterator;
import java.util.LinkedList;

import org.jdom.Element;

import planninglanguage.conditions.AndCondition;
import planninglanguage.conditions.Condition;
import util.XMLWriter;
import base.WargusGameState;
import cbr.CBR;

public abstract class MultiPlan extends Plan implements Cloneable, Iterable<Plan> {
	private LinkedList<Plan> m_subPlans;

	public MultiPlan(Condition a_precondition, Condition a_alivecondition, Condition a_successcondition, Plan a_parent) {
		super(a_precondition, a_alivecondition, a_successcondition);
		m_subPlans = new LinkedList<Plan>();
		m_parent = a_parent;
	}
	public MultiPlan(Condition a_precondition, Condition a_alivecondition, Condition a_successcondition) {
		this(a_precondition, a_alivecondition, a_successcondition, null);
	}
	public MultiPlan() {
		this(null, null, null, null);
	}
	
	public void updateParentHierarchy(Plan parent) {
		setParent(parent);
		for(Plan sp : this) {
			sp.updateParentHierarchy(this);
		}
	}
	
	public void resetPlanStatus(WargusGameState a_currentGameState) {
		resetConditions();
		setStatus(Plan.READY);
		for(Plan sp : this) {
			sp.resetPlanStatus(a_currentGameState);
		}
	}
	
	public void updateStatus(WargusGameState a_currentGameState, long a_curCycle, CBR a_cbr) {
		if (statusComplete()) return;
		
		boolean any_not_succeded = false;
		boolean any_failed = false;	
		boolean some_executing = false;
		
		for(Plan sp : this) sp.updateStatus(a_currentGameState, a_curCycle, a_cbr);	
	
		// Check if all succeeded:
		for(Plan sp : this) {
			if (sp.getStatus() == Plan.EXECUTING) some_executing=true;
			if (sp.getStatus() != Plan.SUCCEEDED) any_not_succeded=true;
		}
		if (!any_not_succeded) {
			setStatus(Plan.SUCCEEDED);					
	//		m_trace.createSuccessEntry(m_currentGameState, (Plan)l_plan,m_pb.getCurrCycle());						
		}				
		if (getStatus() == Plan.READY && some_executing) {
			setStatus(Plan.EXECUTING);
		}
		
		// Check if any failed:
		for(Plan sp : this) if (sp.getStatus() == Plan.FAILED) any_failed=true;				
		if (any_failed) {
			setStatus(Plan.FAILED);
	//		m_trace.createFailureEntry(m_currentGameState, (Plan)l_plan,m_pb.getCurrCycle(),l_plan.getFailedConditions(m_currentGameState), Trace.FailureConditionType.DONTCARE);						
		}
	}
	
	public void substitutePlan(Plan find, Plan replace) {
		for(Plan p2 : this) {
			if (p2 != find) p2.substitutePlan(find, replace);
		}
		if (contains(find)) {
			set(indexOf(find), replace);
		}
	}
	
	abstract protected String formatList(int idx);
	
	public String toString(int tabs) {
		String str= "";
		int i;
		
		for(i=0;i<tabs;i++) str = str + "  ";
		str += getClass().getSimpleName() + "(\n";
		int index = 1;
		for(Plan p : this) {
			str += formatList(index) + p.toString(tabs+1) + "\n";
			index++;
		}
		for(i=0;i<tabs;i++) str = str + "  ";
		str += ") " + conditionsToString(tabs);
		return str;
	}
	
	public Object clone() {
		MultiPlan clone = null;
		try {
			clone = this.getClass().newInstance();
		} catch (Exception e) {
			throw new Error("Can't create new MultiPlan instance!");
		}
		clone.setParent(m_parent);
		// not cloning conditions...
		for(Plan p : m_subPlans){
			Plan planClone = (Plan) p.clone();
			planClone.setParent(clone);
			clone.add(planClone);
		}
		return clone;
	}

	public Iterator<Plan> iterator() {
		return m_subPlans.iterator();
	}

	public int size() {
		return m_subPlans.size();
	}
	public Plan get(int i) {
		return m_subPlans.get(i);
	}
	public boolean add(Plan a_plan) {
		if (a_plan == null) throw new Error("MultiPlan can't have null plans!");
		m_subPlans.add(a_plan);	
		return true;
	}
	public boolean contains(Object p) {
		return m_subPlans.contains(p);
	}
	public int indexOf(Object p) {
		return m_subPlans.indexOf(p);
	}
	public void set(int i, Plan p) {
		m_subPlans.set(i, p);
	}
	public void add(int i, Plan p) {
		if (p == null) throw new Error("MultiPlan can't have null plans!");
		m_subPlans.add(i, p);
	}
	
	// yucki
	public Condition getStateSuccessCondition(WargusGameState a_gamestate) {
		Condition l_success=null;
		for(int i=0;i<m_subPlans.size();i++)
		{
			Plan p = (Plan)(m_subPlans.get(i));
			Condition temp = null;
			String planType = p.getClass().getSimpleName();
			if(planType.equals("ParallelPlan") || planType.equals("SequentialPlan"))
				temp = ((MultiPlan) p).getStateSuccessCondition(a_gamestate);
			else if(planType.equals("SubGoalPlan"))
				temp = ((SubGoalPlan) p).getStateSuccessCondition(a_gamestate);
			else 
				temp = p.getSuccessCondition();
			
			if(i==0)
				l_success = temp;
			else
				l_success = new AndCondition(l_success,temp);
		}
		return l_success;
	}

	public void writeXML(XMLWriter w) {
		w.tag("multi-plan type=\"" + getClass().getSimpleName() + "\""
				+ XMLplanAttributes());
		{
			w.tag("plans");
			{
				for(Plan p : m_subPlans) p.writeXML(w);
			}
			w.tag("/plans");
			this.writePlanAttrsXML(w);
		}
		w.tag("/multi-plan");
	}

	protected static MultiPlan loadMultiPlanFromXML(Element e) {
		MultiPlan plan = null;
		try {
			plan = (MultiPlan) Class.forName("planninglanguage.plans." + 
					e.getAttributeValue("type")).newInstance();
		} catch (Exception ee) {
			throw new Error("Invalid MultiPlan type! " + e.getAttributeValue("type"));
		}
		for (Object o : e.getChild("plans").getChildren()) {
			Element subPlanElement = (Element) o;
			plan.add(Plan.loadPlanFromXML(subPlanElement));			
		}
		Plan.setPlanAttrsFromXML(plan, e);
		return plan;
	}	

}